package com.quyeying.service.impl;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.quyeying.entity.TblOrderBaseEntity;
import com.quyeying.entity.TblUserEntity;
import com.quyeying.service.TblUserService;
import com.quyeying.service.WxService;
import com.quyeying.utils.IdGeneratorSnowflake;
import com.quyeying.utils.httputils.HttpUtil;
import com.quyeying.utils.wx.WxPayConstans;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @author shenkaixin
 */
@Service
public class WxServiceImpl implements WxService {

    @Resource
    TblUserService userService;
    @Resource
    ObjectMapper objectMapper;
    @Resource
    IdGeneratorSnowflake idGeneratorSnowflake;

    /**
     * jsapi下单
     */
    @Override
    public Map<String, Object> miniJsapi(TblOrderBaseEntity orderBaseEntity) throws IOException{
        TblUserEntity user = userService.getById(orderBaseEntity.getUserId());
        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode.put("mchid", WxPayConstans.MCH_ID)
                .put("appid", WxPayConstans.APP_ID)
                .put("description", orderBaseEntity.getOrderNo())
                .put("notify_url", WxPayConstans.NOTIFY_URL)
                .put("out_trade_no", orderBaseEntity.getOrderNo());
        rootNode.putObject("amount")
//                .put("total", 1);
                .put("total", new Integer(orderBaseEntity.getPayerTotal().multiply(new BigDecimal(100)).setScale(0).toString()));
        rootNode.putObject("payer")
                .put("openid", user.getOpenid());

        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        objectMapper.writeValue(bos, rootNode);
        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        CloseableHttpClient httpClient = HttpUtil.getClient();
        CloseableHttpResponse response = httpClient.execute(httpPost);

        String bodyAsString = EntityUtils.toString(response.getEntity());
        boolean has = objectMapper.readTree(bodyAsString).has("prepay_id");
        if (has) {
            String nonceStr = UUID.randomUUID().toString().replace("-", "");

            Map<String, Object> repMap = new HashMap<>(5);
            repMap.put("nonceStr", nonceStr);
            repMap.put("package", "prepay_id=" + objectMapper.readTree(bodyAsString).get("prepay_id").asText());
            repMap.put("signType", "RSA");
            String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
            repMap.put("timeStamp", timeStamp);
//            paySign
            String dataStr = WxPayConstans.APP_ID + "\n" + timeStamp + "\n" + nonceStr + "\n" + "prepay_id=" + objectMapper.readTree(bodyAsString).get("prepay_id").asText() + "\n";
            String sign = sign(dataStr.getBytes(StandardCharsets.UTF_8));
            repMap.put("paySign", sign);
            return repMap;
        }
        return null;
    }

    /**
     * 发起退款
     * @param transactionId
     * @param refundAmount
     */
    @Override
    public JsonNode refund(String transactionId, BigDecimal payerTotal, BigDecimal refundAmount) throws IOException {
        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode.put("transaction_id", transactionId)
                .put("out_refund_no", String.valueOf(idGeneratorSnowflake.snowflakeId()))
                .put("notify_url", WxPayConstans.REFUND_NOTIFY_URL);
        rootNode.putObject("amount")
//                .put("total", 1)
                .put("total", new Integer(payerTotal.multiply(new BigDecimal(100)).setScale(0).toString()))
                .put("refund", new Integer(refundAmount.multiply(new BigDecimal(100)).setScale(0).toString()))
//                .put("refund", 1)
                .put("currency", "CNY");

        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        objectMapper.writeValue(bos, rootNode);

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        CloseableHttpClient httpClient = HttpUtil.getClient();
        CloseableHttpResponse response = httpClient.execute(httpPost);

        String bodyAsString = EntityUtils.toString(response.getEntity());
        return objectMapper.readTree(bodyAsString);
    }

    /**
     * 签名
     * @param message
     * @return
     * @throws Exception
     */
    String sign(byte[] message) {

        try {
            Signature sign = Signature.getInstance("SHA256withRSA");
            PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(WxPayConstans.PRIATE_KEY);
            sign.initSign(merchantPrivateKey);
            sign.update(message);
            return java.util.Base64.getEncoder().encodeToString(sign.sign());
        } catch (SignatureException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        }
        return null;
    }
}